/*
模板编程， 有函数模板与类模

函数模板，建立一个通用函数，其函数返回值类选和形参类选可以不具体指定，用一个虚拟类选来代替
语法
template<typename T>
函数声明或定义

template --- 声明创建模板
typename --- 表面其后面的符号是一种数据类型，可以用class代替
T --- 通用的数据类型，名称可以替换，通常为大写字母

函数模板有两种方式：自动类型推导 显式指定类型
注意事项：
  自动类型推导，必须推导出一致的数据类型T,才可以使用
  模板必须要确定出T的数据类型才可以使用

普通函数与函数模板的区别：
  普通函数调用是会发生自动类型转换（隐式类型转换）
  函数模板调用时，如果利用自动类型推导，不会发生隐式类型转换
  函数模板调用时，如果显式指定类型的方式，可以发生隐式类型转换
*建议使用显示指定类型的方式，调用函数模板，因为可以自己确定通用类型T

普通函数与函数模板的调用规则：
  如果普通函数与函数模板都实现了，优先调用普通函数
  可以通过空模板参数列表来强制调用函数模板
  函数模板也可以发生重载
  如果函数模板产生更好的匹配，优先调用函数模板
*既然提供了函数模板就不要在提供普通函数，容易产生二义性

模板的局限性，模板的通用性不是万能的
例如
template<class T>
void f(T a, T b)
{
    a = b;
}
如果传入的是数组，就无法实现赋值了；
template<class T>
void f(T a, T b)
{
    if(a > b) { ... }
}
如果传入的是一个类，也就无法使用了；
为了解决这些情况，C++提供了模板的重载，可以为这些特性的类型提供提供具体化的模板
*/

#include <iostream>
#include <string>
using namespace std;

template <typename T>
T add(T a, T b)
{
    return a + b;
}

template <typename T>
void fun()
{
    cout << "this is no param template fun..." << endl;
}

// 具体化的模板
class Persion
{
public:
    int m_age;
    string m_name;
    Persion(string name, int age)
    {
        m_age = age;
        m_name = name;
    }
};


// 普通函数模板
template<typename T>
void mycompare(T &a, T &b)
{
    cout << a << "  " << b << endl;
    if (a > b)
    cout << " > " << endl;
    else if (a == b)
    cout << " = " << endl;
    else
    cout << " < " << endl;
}

// Persion类的函数模板
// 具体化，显示具体化的原型和定意思以template<>开头，并通过名称来指出类型 具体化优先于普通模板函数
template<> void mycompare(Persion &p1, Persion &p2)
{
    if (p1.m_age < p2.m_age)
    cout << " < " << endl;
    else if (p1.m_age == p2.m_age)
    cout << " = " << endl;
    else
    cout << " > " << endl;
}


int main()
{
    int a = 1;
    int b = 2;
    int res = add(a, b);
    cout << a << " + " << b << " = " << res << endl;

    float c = 3.3;
    float d = 4.3;
    float resf = add(c, d);
    cout << c << " + " << d << " = " << resf << endl;

    res = add<int>(c, d); // 显式指定类型 会有类型隐式转换
    cout << c << " + " << d << " = " << res << endl;

    // fun();  // 错误，模板不能独立使用，必须确定出T的类型
    fun<int>(); // 利用显式指定给T一个类型就可以使用模板

    // 普通模板函数
    mycompare(a, b);

    // 具体化模板函数
    Persion p1("alise", 18);
    Persion p2("jordan", 14);
    mycompare(p1, p2);
}